﻿using OPCAutomation;
using System;
using System.Collections.Generic;
using System.Linq;
using TYBase;

namespace TYPLC
{
    public delegate void DelegateOnDataChange(string groupName, int index, object value);

    public delegate void DelegateOnOpcMessage(string msg);

    public class TYOpc : IDisposable
    {
        public TYOpc(string section)
        {
            this.Section = section;
        }

        public event DelegateOnDataChange OnDataChange;

        public event DelegateOnOpcMessage OnOpcMessage;

        private const string Node = "127.0.0.1";

        private List<TYOpcGroup> listGroup = new List<TYOpcGroup>();

        private OPCServer m_objOPCServer;
        private OPCGroups ObjOPCGroups;
        private string m_serverName = "KEPware.KEPServerEx.V6";
        public int clientHandle { get; set; }
        public bool IsRun { get; private set; } = true;
        public string Section { get; internal set; }

        public bool IsConnected { get; private set; }



        public bool Connect()
        {
            try
            {
                Log.I("Connect PLC START!");
                IsConnected = false;
                bool isGet = false;
                m_objOPCServer = new OPCServer();
                m_objOPCServer.ServerShutDown += M_objOPCServer_ServerShutDown;
                Log.I("Connect PLC START! 2");
                ObjOPCGroups = m_objOPCServer.OPCGroups;
                Log.I("Connect PLC START! 3");
                object serverList = m_objOPCServer.GetOPCServers(Node);

                Log.I("Connect PLC START!  4");
                foreach (string turn in (Array)serverList)
                {
                    if (turn.ToUpper().Contains("KEPWARE"))
                    {
                        m_serverName = turn;
                        isGet = true;
                        Log.I($"PLC 获取本地OPC服务 :{m_serverName}!");
                        break;
                    }
                }
                if (isGet == false)
                {
                    Log.I("本地OPC无服务!");
                    return false;
                }
                Log.I($"Connect PLC  {m_serverName}!");
                m_objOPCServer.Connect(m_serverName, Node);
                IsConnected = true;
                return true;
            }
            catch (Exception ex)
            {
                Log.E(ex, $"Connect PLC!");
                return false;
            }
        }

        private void M_objOPCServer_ServerShutDown(string Reason)
        {
            IsConnected = true;
        }

        private object lockObj = new object();

        internal bool AddGroup(string groupName, bool isActive, int updateRate, List<string> listAddress)
        {
            if (IsConnected == false)
            {
                Log.I($"初始化异常!");
                return false;
            }
            lock (lockObj)
            {
                if (listGroup.Count(x => x.Name == groupName) >= 1)
                {
                    Log.I($"PLC-  has GroupName {groupName}");
                    return false;
                }
                var ObjOPCGroup = ObjOPCGroups.Add(groupName);
                List<TYOpcItem> listItem = new List<TYOpcItem>();
                listGroup.Add(new TYOpcGroup { Name = groupName, ListItem = listItem, OpcGroup = ObjOPCGroup });
                if (isActive)
                {
                    ObjOPCGroup.DataChange += ObjOPCGroup_DataChange;
                }
                for (int i = 0; i < listAddress.Count; i++)
                {
                    Log.I($"PLC-{listAddress[i]}");
                    OnOpcMessage?.Invoke($"plc -AddGroup groupName:{groupName}:{listAddress[i]}");
                    clientHandle++;
                    var item = ObjOPCGroup.OPCItems.AddItem(listAddress[i], clientHandle);
                    listItem.Add(new TYOpcItem { Address = listAddress[i], ClientHand = clientHandle, Index = i, OpcItem = item });
                }
                ObjOPCGroup.UpdateRate = updateRate;
                ObjOPCGroup.IsActive = isActive;
                ObjOPCGroup.IsSubscribed = true;
                return true;
            }
        }

        private TYOpcItem GetOpcItem(string groupName, int index)
        {
            if (IsConnected == false)
                return null;
            lock (lockObj)
            {
                var group = listGroup.FirstOrDefault(x => x.Name == groupName);
                if (group == null)
                    return null;
                return group.ListItem.FirstOrDefault(x => x.Index == index);
            }
        }

        public bool Write(string groupName, int index, object value)
        {
            if (IsConnected == false)
                return false;
            try
            {
                TYOpcItem item = GetOpcItem(groupName, index);
                if (item == null || item?.OpcItem == null)
                    return false;
                System.Diagnostics.Debug.Print($"plc send {item.Address} {value}");
                item?.OpcItem?.Write(value);

                return true;
            }
            catch (Exception ex)
            {
                Log.E(ex, $"PLC:Write error-->{groupName}-->{index}-->{value}");
                return false;
            }
        }

        public bool Write(string groupName, string address, object value)
        {
            if (IsConnected == false)
                return false;
            try
            {
                var group = listGroup.FirstOrDefault(x => x.Name == groupName);
                if (group == null)
                    return false;
                var item = group.ListItem.FirstOrDefault(x => x.Address == address);
                if (item == null || item?.OpcItem == null)
                    return false;
                item.OpcItem.Write(value);
                return true;
            }
            catch (Exception ex)
            {
                Log.E(ex, $"PLC:Write error-->{groupName}-->{address}-->{value}");
                return false;
            }
        }

        public bool Read(string groupName, string address, out object value)
        {
            if (IsConnected == false)

            {
                value = null;
                return false;
            }
            try
            {
                value = null;
                var group = listGroup.FirstOrDefault(x => x.Name == groupName);
                if (group == null)
                    return false;
                var item = group.ListItem.FirstOrDefault(x => x.Address == address);
                if (item == null)
                    return false;
                object ItemValues;
                object Qualities;
                object TimeStamps;//同步读的临时变量：值、质量、时间戳
                item.OpcItem.Read(2,
                    out ItemValues,
                    out Qualities,
                    out TimeStamps);    //同步读，第一个参数只能为1或2
                value = ItemValues;
                return true;
            }
            catch (Exception ex)
            {
                Log.E(ex, $"PLC:Read error-->{groupName}-->{address}");
                value = null;
                return false;
            }
        }

        public bool Read(string groupName, int index, out object value)
        {
            if (IsConnected == false)

            {
                value = null;
                return false;
            }
            try
            {
                value = null;
                TYOpcItem item = GetOpcItem(groupName, index);
                if (item == null || item?.OpcItem == null)
                    return false;
                object Qualities; object TimeStamps;//同步读的临时变量：值、质量、时间戳
                item.OpcItem.Read(2, out value, out Qualities, out TimeStamps);//同步读，第一个参数只能为1或2
                                                                               //LogHelper.Info($"read over  ItemValues {value}");

                return true;
            }
            catch (Exception ex)
            {
                Log.E(ex, $"PLC:Read error-->{groupName}-->{index}");
                value = null;
                return false;
            }
        }

        //public bool InitPLCData(List<GroupPara> groupParas, ref string error)
        //{
        //    if (this.tagConfigEx == null)
        //        return false;
        //    var listGroup = tagConfigEx.GetGroup();
        //    if (listGroup == null || listGroup.Count == 0)
        //        return false;
        //    foreach (var item in listGroup)
        //    {
        //        List<string> listTags = new List<string>();
        //        foreach (var tagItem in item.ListTag)
        //        {
        //            listTags.Add(tagItem.TagAddress);
        //        }
        //        if (!AddGroup(item.GroupName, item.IsActive, 200, listTags))
        //        {
        //            error = $"初始化PLC {item.GroupName} 失败!";
        //            Log.I($"初始化PLC 异常 :{error}");
        //            return false;
        //        }
        //    }
        //    return true;
        //}

        private void ObjOPCGroup_DataChange(int TransactionID,
            int NumItems,
            ref Array ClientHandles,
            ref Array ItemValues,
            ref Array Qualities,
            ref Array TimeStamps)
        {
            lock (lockObj)
            {
                for (int i = 1; i <= NumItems; i++)
                {
                    int clientHand = Convert.ToInt32(ClientHandles.GetValue(i));
                    foreach (var groupItem in listGroup)
                    {
                        foreach (var tag in groupItem.ListItem)
                        {
                            if (clientHand == tag.ClientHand)
                            {
                                OnDataChange?.BeginInvoke(groupItem.Name, tag.Index, ItemValues.GetValue(i), null, null);
                            }
                        }
                    }
                }
            }
        }

        public void Dispose()
        {
            IsRun = false;
            m_objOPCServer.OPCGroups.RemoveAll();
            m_objOPCServer.Disconnect();
        }
    }
}